package com.uinnova.product.eam.service.impl;

import com.binary.core.exception.BinaryException;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.dao.ArchReviewOrgConfDao;
import com.uinnova.product.eam.dao.ArchReviewOrgDetailConfDao;
import com.uinnova.product.eam.dto.ArchReviewConf;
import com.uinnova.product.eam.dto.SimpOrgInfo;
import com.uinnova.product.eam.dto.SimpUserInfo;
import com.uinnova.product.eam.model.es.ArchReviewOrgConf;
import com.uinnova.product.eam.model.es.ArchReviewOrgDetailConf;
import com.uinnova.product.eam.service.AXEAConfService;
import com.uinnova.product.eam.service.ArchReviewOrgConfService;
import com.uinnova.product.eam.vo.ArchReviewOrgConfInfo;
import com.uinnova.product.eam.vo.ArchReviewOrgConfSaveReq;
import com.uino.api.client.permission.IOrgApiSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.permission.base.SysOrg;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysOrg;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
public class ArchReviewOrgConfServiceImpl implements ArchReviewOrgConfService {

    @Autowired
    private ArchReviewOrgConfDao orgConfDao;
    @Autowired
    private ArchReviewOrgDetailConfDao orgDetailConfDao;
    @Autowired
    private IUserApiSvc userApiSvc;
    @Autowired
    private IOrgApiSvc orgService;
    @Autowired
    private AXEAConfService axeaConfService;

    @Override
    public void save(List<ArchReviewOrgConfSaveReq> reqs) {
        Assert.notEmpty(reqs, "团队不能为空");
        int paramsOrgCount = 0;
        Set<Long> orgIds = new HashSet<>();
        Set<String> persions = new HashSet<>();
        for (ArchReviewOrgConfSaveReq req : reqs) {
            Assert.notEmpty(req.getOrgIds(), "团队不能为空");
            Assert.notEmpty(req.getPersions(), "评审名单不能为空");
            Assert.notNull(req.getRule(), "评审规则不能为空");
            orgIds.addAll(req.getOrgIds());
            paramsOrgCount += req.getOrgIds().size();
            persions.addAll(req.getPersions());
        }
        if (paramsOrgCount != orgIds.size()) {
            throw new BinaryException("团队配置重复，请核查");
        }
        List<SysOrg> orgs = getOrgByIds(orgIds);
        if (CollectionUtils.isEmpty(orgs) || orgIds.size() != orgs.size()) {
            throw new BinaryException("部分团队不存在，请刷新列表重试");
        }
        Map<Long, SysOrg> orgMap = orgs.stream().collect(Collectors.toMap(SysOrg::getId, e -> e, (k1,k2) -> k1));
        Set<Long> parentOrgIds = orgs.stream().map(SysOrg::getParentOrgId).collect(Collectors.toSet());
        List<SysOrg> parentOrgs = getOrgByIds(parentOrgIds);
        if (CollectionUtils.isEmpty(parentOrgs) || parentOrgIds.size() != parentOrgs.size()) {
            throw new BinaryException("部分团队无上级组织，请刷新列表重试");
        }
        Map<Long, SysOrg> parentOrgMap = parentOrgs.stream().collect(Collectors.toMap(SysOrg::getId, e -> e, (k1,k2) -> k1));
        List<SysUser> users = getUserByLoginCodes(persions);
        if (CollectionUtils.isEmpty(users) || persions.size() != users.size()) {
            throw new BinaryException("部分用户不存在，请刷新列表重试");
        }
        Map<String, SysUser> userMap = users.stream().collect(Collectors.toMap(SysUser::getLoginCode, e -> e, (k1,k2) -> k1));
        Long archReviewConfId = ESUtil.getUUID();
        List<ArchReviewOrgDetailConf> detailConfs = new ArrayList<>();
        String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        for (ArchReviewOrgConfSaveReq req : reqs) {
            ArchReviewOrgDetailConf detailConf = new ArchReviewOrgDetailConf();
            detailConf.setOrgConfId(archReviewConfId);
            detailConf.setId(ESUtil.getUUID());
            detailConf.setCreatorCode(curUserLoginCode);
            detailConf.setCreateTime(ESUtil.getNumberDateTime());
            detailConf.setModifierCode(curUserLoginCode);
            detailConf.setModifyTime(ESUtil.getNumberDateTime());
            List<SimpOrgInfo> orgInfos = new ArrayList<>();
            for (Long orgId : req.getOrgIds()) {
                if (!orgMap.containsKey(orgId)) {
                    throw new BinaryException("部分团队不存在，请刷新列表重试");
                }
                SysOrg org = orgMap.get(orgId);
                if (!parentOrgMap.containsKey(org.getParentOrgId())) {
                    throw new BinaryException("部分团队无上级组织，请刷新列表重试");
                }
                SimpOrgInfo orgInfo = new SimpOrgInfo();
                orgInfo.setOrgId(orgId);
                orgInfo.setOrgName(parentOrgMap.get(org.getParentOrgId()).getOrgName().concat("/").concat(org.getOrgName()));
                orgInfos.add(orgInfo);
            }
            detailConf.setOrgIds(req.getOrgIds());
            detailConf.setOrgInfos(orgInfos);
            List<SimpUserInfo> userInfos = new ArrayList<>();
            for (String persion : req.getPersions()) {
                if (!userMap.containsKey(persion)) {
                    throw new BinaryException("部分用户不存在，请刷新列表重试");
                }
                userInfos.add(new SimpUserInfo(persion, userMap.get(persion).getUserName()));
            }
            detailConf.setPersions(req.getPersions());
            detailConf.setPersionInfos(userInfos);
            detailConf.setRule(req.getRule());
            detailConfs.add(detailConf);
        }

        ArchReviewOrgConf orgConf = new ArchReviewOrgConf();
        orgConf.setId(archReviewConfId);
        orgConf.setVersion(getMaxVersion() + 1);
        orgConf.setCreatorCode(curUserLoginCode);
        orgConf.setCreateTime(ESUtil.getNumberDateTime());
        orgConf.setModifierCode(curUserLoginCode);
        orgConf.setModifyTime(ESUtil.getNumberDateTime());
        orgDetailConfDao.saveOrUpdateBatch(detailConfs);
        orgConfDao.saveOrUpdate(orgConf);
    }

    private Long getMaxVersion() {
        Page<ArchReviewOrgConf> page = orgConfDao.getSortListByQuery(1, 1, QueryBuilders.boolQuery(), "version", false);
        return CollectionUtils.isEmpty(page.getData()) ? 0L : page.getData().get(0).getVersion();
    }

    private List<SysUser> getUserByLoginCodes(Set<String> loginCodes) {
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodes(loginCodes.toArray(new String[loginCodes.size()]));
        return userApiSvc.getSysUserByCdt(cdt);
    }

    private List<SysOrg> getOrgByIds(Set<Long> orgIds) {
        CSysOrg query = new CSysOrg();
        query.setIds(orgIds.toArray(new Long[orgIds.size()]));
        return orgService.queryListByCdt(query);
    }

    @Override
    public List<ArchReviewOrgConfInfo> info() {
        TermQueryBuilder confQuery = QueryBuilders.termQuery("version", getMaxVersion());
        List<ArchReviewOrgConf> confs = orgConfDao.getListByQuery(confQuery);
        if (CollectionUtils.isEmpty(confs)) {
            return new ArrayList<>();
        }
        Long orgConfId = confs.get(0).getId();
        TermQueryBuilder confDetailQuery = QueryBuilders.termQuery("orgConfId", orgConfId);
        List<SortBuilder<?>> sorts = new ArrayList<>();
        FieldSortBuilder createTimeSortBuilder = SortBuilders.fieldSort("createTime").order(SortOrder.DESC);
        sorts.add(createTimeSortBuilder);
        Page<ArchReviewOrgDetailConf> page = orgDetailConfDao.getSortListByQuery(1, 3000, confDetailQuery, sorts);
        if (CollectionUtils.isEmpty(page.getData())) {
            return new ArrayList<>();
        }
        List<ArchReviewOrgDetailConf> detailConfs = page.getData();
        Set<Long> orgIds = new HashSet<>();
        Map<Long, SimpOrgInfo> redundanceOrgMap = new ConcurrentHashMap<>();
        Set<String> persions = new HashSet<>();
        Map<String, SimpUserInfo> redundancePersionMap = new ConcurrentHashMap<>();
        for (ArchReviewOrgDetailConf detailConf : detailConfs) {
            orgIds.addAll(detailConf.getOrgIds());
            redundanceOrgMap.putAll(detailConf.getOrgInfos().stream().collect(Collectors.toMap(SimpOrgInfo::getOrgId, e -> e)));
            persions.addAll(detailConf.getPersions());
            redundancePersionMap.putAll(detailConf.getPersionInfos().stream().collect(Collectors.toMap(SimpUserInfo::getLoginCode, e -> e)));
        }
        Map<Long, SysOrg> dbOrgMap = new ConcurrentHashMap<>();
        Map<String, SimpUserInfo> dbPersionMap = new ConcurrentHashMap<>();
        Map<Long, SysOrg> parentOrgMap = new ConcurrentHashMap<>();
        List<SysOrg> orgs = getOrgByIds(orgIds);
        if (!CollectionUtils.isEmpty(orgs)) {
            dbOrgMap = orgs.stream().collect(Collectors.toMap(SysOrg::getId, e -> e, (k1,k2) -> k1));
        }
        Set<Long> parentOrgIds = orgs.stream().map(SysOrg::getParentOrgId).collect(Collectors.toSet());
        List<SysOrg> parentOrgs = getOrgByIds(parentOrgIds);
        if (!CollectionUtils.isEmpty(parentOrgs) ) {
            parentOrgMap = parentOrgs.stream().collect(Collectors.toMap(SysOrg::getId, e -> e, (k1,k2) -> k1));
        }
        List<SysUser> users = getUserByLoginCodes(persions);
        if (!CollectionUtils.isEmpty(users)) {
            dbPersionMap = users.stream().collect(Collectors.toMap(SysUser::getLoginCode,
                    e -> new SimpUserInfo(e.getLoginCode(), e.getUserName()), (k1,k2) -> k1));
        }
        List<ArchReviewOrgConfInfo> infos = new ArrayList<>();
        for (ArchReviewOrgDetailConf detailConf : detailConfs) {
            ArchReviewOrgConfInfo confInfo = new ArchReviewOrgConfInfo();
            confInfo.setOrgConfId(detailConf.getOrgConfId());
            confInfo.setOrgIds(detailConf.getOrgIds());
            List<SimpOrgInfo> orgInfos = new ArrayList<>();
            for (Long orgId : detailConf.getOrgIds()) {
                String orgName = redundanceOrgMap.get(orgId).getOrgName();
                if (dbOrgMap.containsKey(orgId) && parentOrgMap.containsKey(dbOrgMap.get(orgId).getParentOrgId())) {
                    SysOrg dbOrg = dbOrgMap.get(orgId);
                    orgName = parentOrgMap.get(dbOrg.getParentOrgId()).getOrgName().concat("/").concat(dbOrg.getOrgName());
                }
                SimpOrgInfo orgInfo = new SimpOrgInfo();
                orgInfo.setOrgId(orgId);
                orgInfo.setOrgName(orgName);
                orgInfos.add(orgInfo);
            }
            confInfo.setOrgInfos(orgInfos);
            confInfo.setPersions(detailConf.getPersions());
            List<SimpUserInfo> userInfos = new ArrayList<>();
            for (String persion : detailConf.getPersions()) {
                String userName = dbPersionMap.getOrDefault(persion, redundancePersionMap.get(persion)).getUserName();
                userInfos.add(new SimpUserInfo(persion, userName));
            }
            confInfo.setPersionInfos(userInfos);
            confInfo.setRule(detailConf.getRule());
            infos.add(confInfo);
        }
        return infos;
    }

    @Override
    public List<SimpOrgInfo> orgs() {
        ArchReviewConf reviewConf = axeaConfService.getArchReviewConf();
        List<Long> archReviewOrgIds = reviewConf.getArchReviewOrgIds();
        List<SysOrg> parentOrgs = getOrgByIds(new HashSet<>(archReviewOrgIds));
        if (CollectionUtils.isEmpty(parentOrgs)) {
            return new ArrayList<>();
        }
        Map<Long, SimpOrgInfo> parentOrgMap = parentOrgs.stream().collect(
                Collectors.toMap(SysOrg::getId, e -> new SimpOrgInfo(e.getId(), e.getOrgName())));
        CSysOrg query = new CSysOrg();
        query.setParentOrgIds(archReviewOrgIds.toArray(new Long[archReviewOrgIds.size()]));
        List<SysOrg> orgs = orgService.queryListByCdt(query);
        if (CollectionUtils.isEmpty(orgs)) {
            return new ArrayList<>();
        }
        List<SimpOrgInfo> orgInfos = new ArrayList<>();
        for (SysOrg org : orgs) {
            SimpOrgInfo orgInfo = new SimpOrgInfo();
            orgInfo.setOrgId(org.getId());
            if (org.getParentOrgId() == null
                    || !parentOrgMap.containsKey(org.getParentOrgId())) {
                continue;
            }
            SimpOrgInfo parentOrg = parentOrgMap.get(org.getParentOrgId());
            orgInfo.setOrgName(parentOrg.getOrgName().concat("/").concat(org.getOrgName()));
            orgInfos.add(orgInfo);
        }
        return orgInfos;
    }

    @Override
    public ArchReviewOrgDetailConf getLatestByOrgId(Long orgId) {
        TermQueryBuilder confQuery = QueryBuilders.termQuery("version", getMaxVersion());
        List<ArchReviewOrgConf> confs = orgConfDao.getListByQuery(confQuery);
        if (CollectionUtils.isEmpty(confs)) {
            return null;
        }
        Long orgConfId = confs.get(0).getId();
        TermQueryBuilder confDetailQuery = QueryBuilders.termQuery("orgConfId", orgConfId);
        List<SortBuilder<?>> sorts = new ArrayList<>();
        FieldSortBuilder createTimeSortBuilder = SortBuilders.fieldSort("createTime").order(SortOrder.DESC);
        sorts.add(createTimeSortBuilder);
        Page<ArchReviewOrgDetailConf> page = orgDetailConfDao.getSortListByQuery(1, 3000, confDetailQuery, sorts);
        if (CollectionUtils.isEmpty(page.getData())) {
            return null;
        }
        for (ArchReviewOrgDetailConf orgDetailConf : page.getData()) {
            if (orgDetailConf.getOrgIds().contains(orgId)) {
                return orgDetailConf;
            }
        }
        return null;
    }
}
